using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using Neo.MetaModel;
using NVelocity;
using NVelocity.App;
using NVelocity.Runtime;


namespace Neo.Generator.Core
{	
	///<summary>Code Generator
	///</summary>
	public class VelocityGenerator
	{

		//--------------------------------------------------------------------------------------
		//	fields and constructor
		//--------------------------------------------------------------------------------------
		
		private static bool	velocityInitialized;
		/// <summary>
		/// THe Reader Type
		/// </summary>
		protected Type		readerType;
        /// <summary>
        /// THe Template to use
        /// </summary>
		protected string	template;
        /// <summary>
        /// Additional properties to pass to the context
        /// </summary>
        protected StringDictionary properties;


        /// <summary>
        /// Initializes a new instance of the <see cref="VelocityGenerator"/> class.
        /// </summary>
		public VelocityGenerator()
		{
			Velocity.SetProperty("runtime.log.logsystem.class", "NVelocity.Runtime.Log.NullLogSystem");
			Velocity.SetProperty(RuntimeConstants.RUNTIME_LOG_ERROR_STACKTRACE, "true");
			Velocity.SetProperty(RuntimeConstants.RUNTIME_LOG_WARN_STACKTRACE, "true");
			Velocity.SetProperty(RuntimeConstants.RUNTIME_LOG_INFO_STACKTRACE, "true");
			Velocity.SetProperty(RuntimeConstants.UBERSPECT_CLASSNAME, "NVelocity.Util.Introspection.UberspectImpl");
			Velocity.SetProperty("directive.manager", "NVelocity.Runtime.Directive.DirectiveManager; NVelocity");
            Properties = new StringDictionary();
		}


		//--------------------------------------------------------------------------------------
		//	properties
		//--------------------------------------------------------------------------------------

        /// <summary>
        /// Sets the resource path.
        /// </summary>
        /// <value>The resource path.</value>
		public string ResourcePath
		{
			set { Velocity.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, value); }
		}

        /// <summary>
        /// Gets or sets the type of the reader.
        /// </summary>
        /// <value>The type of the reader.</value>
		public Type ReaderType
		{
			set { readerType = value; }
			get { return readerType; }
		}

        /// <summary>
        /// Gets or sets the template.
        /// </summary>
        /// <value>The template.</value>
		public string Template
		{
			set { template = value; }
			get { return template; }
		}

        /// <summary>
        /// Gets or sets the properties.
        /// </summary>
        /// <value>The properties.</value>
        public StringDictionary Properties
        {
            get { return properties; }
            set { properties = value; }
        }

		//--------------------------------------------------------------------------------------
		//	generate files
		//--------------------------------------------------------------------------------------

        /// <summary>
        /// Generates the specified input path.
        /// </summary>
        /// <param name="inputPath">The input path.</param>
        /// <param name="outputFile">The output file.</param>
        /// <returns></returns>
		public virtual IList Generate(string inputPath, string outputFile)
		{
			IModelReader modelReader;
			TextWriter	 writer;

			modelReader = (IModelReader)Activator.CreateInstance(readerType);
			modelReader.ReadConfiguration(inputPath, new ConfigDelegate(SetConfigurationAttribute));
			modelReader.LoadModel(inputPath);

			Console.WriteLine("Writing {0}", outputFile);
			
			writer = new StreamWriter(outputFile);
			GenerationContext ctx = new GenerationContext(modelReader.Model);
			Generate(template, ctx, writer);   		
			writer.Close();

			return new ArrayList(new object[] { outputFile });
		}


        /// <summary>
        /// Generates the specified template name.
        /// </summary>
        /// <param name="templateName">Name of the template.</param>
        /// <param name="neoContext">The neo context.</param>
        /// <param name="writer">The writer.</param>
		protected virtual void Generate(string templateName, GenerationContext neoContext, TextWriter writer)
		{
			VelocityContext	  velocityContext;

			if(velocityInitialized == false)
			{
				velocityInitialized = true;
				Velocity.Init();
			}
			velocityContext = new VelocityContext();
			velocityContext.Put("Neo", neoContext);
			Velocity.MergeTemplate(templateName, "ISO-8859-1", velocityContext, writer);
		}
		
		
		//--------------------------------------------------------------------------------------
		//	helper methods
		//--------------------------------------------------------------------------------------

        /// <summary>
        /// Sets the configuration attribute.
        /// </summary>
        /// <param name="inputPath">The input path.</param>
        /// <param name="attr">The attr.</param>
        /// <param name="val">The val.</param>
		protected virtual void SetConfigurationAttribute(string inputPath, string attr, string val)
		{
			switch(attr)
			{
				case "path":
					ResourcePath = Path.Combine(Path.GetDirectoryName(inputPath), val);
					break;
				default:
                    properties[attr] = val;
                    break;
			}
		}

	
	}
}
